Preliminary analysis of the SRP358696 dataset, which contain Wilms tumor samples.

set.seed(123)

library(DESeq2)
library(fgsea)
library(ggplot2)
library(ggrepel)
library(RColorBrewer)
library(patchwork)
library(ggplotify)
library(EnhancedVolcano)
library(ggvenn)
source("/storage/research/dbmr_rubin_lab/scripts/save_png_pdf.R")
source("/storage/research/dbmr_rubin_lab/scripts/fgsea_save_res.R")

plotdir=("plots/")
if(!dir.exists(plotdir)){dir.create(plotdir,  recursive = T)}

Load sample names

samples <- scan("../samples.txt", character())

Load rsem counts

rsem_res_coding <- read.table("../rsem/all.genes.expected_count.results_coding", header = T, sep = "\t", row.names=1, check.names = F)

rsem_res_coding$gene_name_uniq <- make.unique(rsem_res_coding$gene_name)

remove metadata cols (note new rsem has more cols) last col is gene_name_uniq

counts <- rsem_res_coding[c(-1:-27, -ncol(rsem_res_coding))]

rownames(counts) <- rsem_res_coding$gene_name_uniq

counts need to be integers

counts <- round(counts)

remove low counts

counts <- counts[rowSums(counts) > 5,]

Load metadatra

meta <- openxlsx::read.xlsx("../GSE200256_metadata_bulk_RNA_seq.xlsx")

meta$sample <- sub("_", "", meta[["Sample.Name"]])

assure same order

identical(meta$sample, colnames(counts))
[1] TRUE

DESeq2

set coldata

coldata <- data.frame(condition = gsub(" .+", "", meta$isolate),
                        tissue = meta$tissue,
                        sex = meta$sex,
                    row.names =  colnames(counts))

construct a DESeqDataSet

dds <- DESeqDataSetFromMatrix(countData = counts,
                            colData = coldata,
                            design = ~ condition + tissue + sex)
#relevel
relevel(dds$condition, ref = "Favorable")

run DGE inference

dds <- DESeq(dds)

Variance stabilizing transformation

vsd <- vst(dds, blind=FALSE)

z-scores of vst data (for visualisation etc.)

vsd.zscore <- as.data.frame(t(scale(t(assay(vsd)), scale=TRUE, center=TRUE)))

Heatmap of the sample-to-sample distances

sampleDists <- dist(t(assay(vsd)))
sampleDistMatrix <- as.matrix(sampleDists)

colors <- colorRampPalette(rev(brewer.pal(9, "Blues")) )(255)

# use fixed cell sizes to look symmetrical, then adjust figure size to have proper margins
p <- pheatmap::pheatmap(sampleDistMatrix,
        clustering_distance_rows=sampleDists,
        clustering_distance_cols=sampleDists,
        col=colors,
        annotation = coldata,
        main = "Sample distance\n",
        cellwidth = 12, cellheight = 12,
        silent = T)

save_png_pdf(p, paste0(plotdir, "heatmap"), height = 12, width = 12)

PCA

pca <- list("PC1-PC2" = 1:2,
            "PC3-PC4" = 3:4,
            "PC5-PC6" = 5:6,
            "PC7-PC8" = 7:8,
            "PC9-PC10" = 9:10)

for (x in names(pca)) {
    cat(x, "\n")
    pcaData <- plotPCA(vsd, intgroup=c("condition"), returnData=TRUE, pcsToUse = pca[[x]])
    percentVar <- round(100 * attr(pcaData, "percentVar"))
    
    p <- ggplot(pcaData, aes(!!sym(paste0("PC", pca[[x]][1])), !!sym(paste0("PC", pca[[x]][2])), fill=condition, color=tissue, shape=sex)) +
        geom_point(size=3, stroke = 1.2) +
        scale_shape_manual(values = 21:25) + # use fillable shapes
        scale_color_brewer(type = "qual", palette = "Set1",
                        guide = guide_legend(override.aes = list(shape = 1))) + # use a hollow shape in legend
        scale_fill_manual(values = c("Anaplastic" = "grey20", "Favorable" = "grey90"),
                        guide = guide_legend(override.aes = list(shape = 23, stroke=0.5))) + # use fillable shape in legend
        xlab(paste0("PC",pca[[x]][1],": ",percentVar[1],"% variance")) +
        ylab(paste0("PC",pca[[x]][2],": ",percentVar[2],"% variance")) +
        xlim(c(-max(abs(pcaData[[paste0("PC", pca[[x]][1])]]))-1, max(abs(pcaData[[paste0("PC", pca[[x]][1])]]))+1)) + # PC can be small that dots get trimmed. Expand a bit.
        ylim(c(-max(abs(pcaData[[paste0("PC", pca[[x]][2])]]))-1, max(abs(pcaData[[paste0("PC", pca[[x]][2])]]))+1)) + # PC can be small that dots get trimmed. Expand a bit.
        geom_text_repel(aes(label = name),
                        size = 1.5,
                        segment.size = 0.1,
                        colour = 'grey50',
                        box.padding   = 0.10,
                        segment.color = 'grey50',
                        force = 50,
                        max.overlaps = 30,
                        show.legend = F) +
        theme_minimal()
    
    save_png_pdf(p, paste0(plotdir, x), height = 5, width = 8)
    print(p)
}
PC1-PC2 
PC3-PC4 
PC5-PC6 
PC7-PC8 
PC9-PC10 

verify comparisons

resultsNames(dds)
[1] "Intercept"                         "condition_Favorable_vs_Anaplastic" "tissue_Kidney_vs_Diaphram"        
[4] "tissue_Left.Kidney_vs_Diaphram"    "tissue_Liver_vs_Diaphram"          "tissue_Right.Kidney_vs_Diaphram"  
[7] "sex_male_vs_female"               

Differential expression results for each comparison There is only 1 comparison

c <- "Anaplastic_vs_Favorable"
res <- results(dds, contrast = c("condition", "Anaplastic", "Favorable"), alpha = 0.05)

MA-plot

save_png_pdf(plotMA(res, ylim=c(-10,10)), paste0(plotdir, "DESeq2_res.", c, ".MA-plot"))

res to table, and add gene info

res.df <- as.data.frame(res)


res.df <- merge(res.df, rsem_res_coding[,c("gene_name_uniq", "gene_name", "gene_id", "gene_type", "seqnames", "start", "end", "width", "strand")], by.x=0, by.y="gene_name_uniq", all.x = T, all.y = F, sort = F)
colnames(res.df)[1] <- "gene_name_uniq"

save results

openxlsx::write.xlsx(res.df, file="DESeq2_res.xlsx")

volcano plots

EnhancedVolcano

p <- EnhancedVolcano(res.df,
                    lab = res.df$gene_name,
                    x = 'log2FoldChange',
                    y = 'padj',
                    ylab = bquote(~-Log[10] ~ italic(Padj)),
                    title = c,
                    subtitle = NULL,
                    cutoffLineCol = "gray10",
                    cutoffLineWidth = 0.2,
                    pointSize = 2,
                    labSize = 3,
                    axisLabSize = 15,
                    titleLabSize = 15,
                    labCol = "gray30",
                    col = c("grey60", "slategray3", "lightpink", "tomato"),
                    legendPosition = 'right',
                    legendLabels=c("NS", expression(Log[2] ~ FC), "Padj", expression(Padj ~ and ~ Log[2] ~ FC)),
                    legendLabSize = 10,
                    max.overlaps = 50,
                    drawConnectors = T,
                    widthConnectors = 0.1,
                    arrowheads = F,
                    colConnectors = "grey30",
                    directionConnectors="both" # 'y' works ok for fewer genes. ideal should be upregulated to one side, down to the other
                    ) +
    theme_minimal()

save_png_pdf(p, paste0(plotdir, "EnhancedVolcano.", c), height = 6, width = 8)

fgsea Msigdb

Load get gene sets in gmt

msigdb.hs.gmt <- readRDS("/storage/research/dbmr_rubin_lab/resources/msigdb/msigdb.hs.gmt.rds")
msigdb.hs.info <- readRDS("/storage/research/dbmr_rubin_lab/resources/msigdb/msigdb.hs.info.df.rds")

make rank

res.rank <- setNames(res.df$stat, make.unique(res.df$gene_name))

run only relevant collections

collections <- c("C2.CP", "C2.CP:KEGG_LEGACY", "C2.CP:KEGG_MEDICUS", "C2.CP:REACTOME", "C4.3CA", "C4.CGN", "C4.CM", "C5.GO:BP", "C5.GO:MF", "C6", "C7.IMMUNESIGDB", "C8", "H")

run fgsea

res.fgseaRes.msigdb <- sapply(collections, function(x) {
    cat(c, x, "\n")
    fgsea(pathways = msigdb.hs.gmt[[x]],
            stats = res.rank,
            eps = 0.0, minSize = 15, maxSize = 500)
}, simplify=F)

# Save session
save.image(file="session.RData")

Plot fgsea results

fgsea_save_res(res.fgseaRes.msigdb,
                rank = res.rank,
                basename = "fgsea.",
                gmt = msigdb.hs.gmt[collections],
                gmt_info = msigdb.hs.info,
                suffix = c,
                subtitle = c)

DE on transcript level

load rsem counts

rsem_res_coding.transcript <- read.table("../rsem/all.isoforms.expected_count.results_coding", header = T, sep = "\t", row.names=1, check.names = F)

# remove metadata cols (note new rsem has more cols)
counts.transcript <- rsem_res_coding.transcript[-1:-27]

# rownames can be transcript_name (they are unique)
sum(duplicated(rsem_res_coding.transcript$transcript_name))
# [1] 0
# but replace "-" with "_" so DESeq2 does not complain
rownames(counts.transcript) <- sub("-", "_", rsem_res_coding.transcript$transcript_name)

Prepare counts

# counts.transcript need to be integers
counts.transcript <- round(counts.transcript)

# remove low counts.transcript
counts.transcript <- counts.transcript[rowSums(counts.transcript) > 5,]

DESeq2

construct a DESeqDataSet

dds.transcript <- DESeqDataSetFromMatrix(countData = counts.transcript,
                            colData = coldata,
                            design = ~ condition + tissue + sex) 

# relevel
relevel(dds.transcript$condition, ref = "Favorable")

run DGE inference

dds.transcript <- DESeq(dds.transcript)

Variance stabilizing transformation

vsd.transcript <- vst(dds.transcript, blind=FALSE)

z-scores of vst data (for visualisation etc.)

vsd.transcript.zscore <- as.data.frame(t(scale(t(assay(vsd.transcript)), scale=TRUE, center=TRUE)))

Heatmap of the sample-to-sample distances

sampleDists <- dist(t(assay(vsd.transcript)))
sampleDistMatrix <- as.matrix(sampleDists)

colors <- colorRampPalette(rev(brewer.pal(9, "Blues")) )(255)

# use fixed cell sizes to look symmetrical, then adjust figure size to have proper margins
p <- pheatmap::pheatmap(sampleDistMatrix,
        clustering_distance_rows=sampleDists,
        clustering_distance_cols=sampleDists,
        col=colors,
        annotation = coldata,
        main = "Sample distance\n",
        cellwidth = 12, cellheight = 12,
        silent=T)

save_png_pdf(p, paste0(plotdir, "heatmap.transcript"), height = 12, width = 12)

PCA

pcaData <- plotPCA(vsd.transcript, intgroup=c("condition"), returnData=TRUE)
percentVar <- round(100 * attr(pcaData, "percentVar"))

p <- ggplot(pcaData, aes(PC1, PC2, fill=condition, color=tissue, shape=sex)) +
    geom_point(size=3, stroke = 1.2) +
    scale_shape_manual(values = 21:25) + # use fillable shapes
    scale_color_brewer(type = "qual", palette = "Set1",
                    guide = guide_legend(override.aes = list(shape = 1))) + # use a hollow shape in legend
    scale_fill_manual(values = c("Anaplastic" = "grey20", "Favorable" = "grey90"),
                    guide = guide_legend(override.aes = list(shape = 23, stroke=0.5))) + # use fillable shape in legend
    xlab(paste0("PC1: ",percentVar[1],"% variance")) +
    ylab(paste0("PC2: ",percentVar[2],"% variance")) +
    ylim(c(-max(abs(pcaData$PC2))-1, max(abs(pcaData$PC2))+1)) + # if PC2 is small dots get trimmed. Expand a bit.
    geom_text_repel(aes(label = name),
                    size = 1.5,
                    segment.size = 0.1,
                    colour = 'grey50',
                    box.padding   = 0.10,
                    segment.color = 'grey50',
                    force = 50,
                    max.overlaps = 30,
                    show.legend = F) +
    theme_minimal()

save_png_pdf(p, paste0(plotdir, "PCA.transcript"), height = 5, width = 8)
null device 
          1 
print(p)

Fetch results

res.transcript <- results(dds.transcript, contrast = c("condition", "Anaplastic", "Favorable"), alpha = 0.05)

MA-plot

save_png_pdf(plotMA(res.transcript, ylim=c(-20,20)), paste0(plotdir, "DESeq2_res.transcript.", c, ".MA-plot"))

res to table, and add gene info

res.transcript.df <- as.data.frame(res.transcript)

# _ to - so that we can merge
res.transcript.df$transcript_name <- sub("_", "-", rownames(res.transcript))

res.transcript.df <- merge(res.transcript.df, rsem_res_coding.transcript[,c("transcript_name", "transcript_id", "transcript_type", "gene_name", "gene_id", "seqnames", "start", "end", "width", "strand")], by="transcript_name", all.x = T, all.y = F, sort = F)

save results

openxlsx::write.xlsx(res.transcript.df, file="DESeq2_res.transcript.xlsx")

volcano plots

EnhancedVolcano

p <- EnhancedVolcano(res.transcript.df,
                    lab = res.transcript.df$transcript_name,
                    x = 'log2FoldChange',
                    y = 'padj',
                    ylab = bquote(~-Log[10] ~ italic(Padj)),
                    title = c,
                    subtitle = NULL,
                    cutoffLineCol = "gray10",
                    cutoffLineWidth = 0.2,
                    pointSize = 2,
                    labSize = 2,
                    axisLabSize = 15,
                    titleLabSize = 15,
                    labCol = "gray30",
                    col = c("grey60", "slategray3", "lightpink", "tomato"),
                    legendPosition = 'right',
                    legendLabels=c("NS", expression(Log[2] ~ FC), "Padj", expression(Padj ~ and ~ Log[2] ~ FC)),
                    legendLabSize = 10,
                    max.overlaps = 50,
                    drawConnectors = T,
                    widthConnectors = 0.1,
                    arrowheads = F,
                    colConnectors = "grey30",
                    ) +
    theme_minimal()

save_png_pdf(p, paste0(plotdir, "EnhancedVolcano.transcript.", c), height = 6, width = 8)

Kallisto

load kallisto counts over transcripts

kallisto_res.transcript <- read.table("../kallisto/all.est_count.txt.gz", header = T, sep = "\t", row.names=1, check.names = F)[-1] # omit column "length"

use tximport to aggregate to gene level

Prepare data

read the gtf used for the Kallisto index (full anno, not just “annotation” or “primary_assembly.annotation”)

gencode.v48 <- rtracklayer::readGFF("/storage/research/dbmr_rubin_lab/pipeline/ref/anno/hg38/gencode.v48.chr_patch_hapl_scaff.annotation.gtf.gz")

did we fetch everything?

sum(!rownames(kallisto_res.transcript) %in% gencode.v48$transcript_id)
[1] 0

more sanity checks

rsem_transcript_list <- read.table("../rsem/all.isoforms.expected_count.results", header = T, sep = "\t")[[1]]
sum(rsem_transcript_list %in% gencode.v48$transcript_id)
[1] 387954
length(rsem_transcript_list)
[1] 387954

make a data.frame called tx2gene with two columns: 1) transcript ID and 2) gene ID. The column names do not matter but this column order must be used.

tx2gene <- gencode.v48[c("transcript_id", "gene_id")]

# remove NA and duplicate transcripts
tx2gene <- na.omit(tx2gene)
tx2gene <- tx2gene[!duplicated(tx2gene$transcript_id),]

gene counts from kallisto TSV files

k_files <- file.path("../kallisto", samples, "abundance.tsv.gz")
names(k_files) <- samples

tximport to aggregate to gene level

# we don't need ignoreAfterBar
kallisto_res.gene <- tximport::tximport(k_files, type = "kallisto", tx2gene = tx2gene)

# to df
kallisto_res.gene.df <- as.data.frame(kallisto_res.gene[["abundance"]])

compare rsem and kallisto

rsem_res <- read.table("../rsem/all.genes.expected_count.results", header = T, sep = "\t", row.names=1, check.names = F)[c(-1:-27)] # omit metadata cols

rsem_res.transcript <- read.table("../rsem/all.isoforms.expected_count.results", header = T, sep = "\t", row.names=1, check.names = F)[c(-1:-27)] # omit metadata cols

sanity check

identical(colnames(rsem_res.transcript), colnames(kallisto_res.transcript))
[1] TRUE

Note: kallisto has more genes/transcripts than rsem (which are all in kallisto)

rsem_vs_kallisto <- list(transcript = cor(rsem_res.transcript, kallisto_res.transcript[rownames(rsem_res.transcript),], method = "spearman"))

rsem_vs_kallisto[["transcript.coding"]] <- cor(rsem_res_coding.transcript[-1:-27], kallisto_res.transcript[rownames(rsem_res_coding.transcript),], method = "spearman")

rsem_vs_kallisto[["gene"]] <- cor(rsem_res, kallisto_res.gene.df[rownames(rsem_res),], method = "spearman")

rsem_vs_kallisto[["gene.coding"]] <- cor(rsem_res[rownames(rsem_res_coding),], kallisto_res.gene.df[rownames(rsem_res_coding),], method = "spearman")

Plot correlatoions

for (x in names(rsem_vs_kallisto)) {
    p <- ComplexHeatmap::pheatmap(rsem_vs_kallisto[[x]],
        cluster_rows = F,
        cluster_cols = F,
        main = paste0("RSEM vs. Kallisto", "\n", "counts", "\n", x),
        fontsize_row = 8,
        fontsize_col = 8,
        cellheight = 12,
        cellwidth = 12,
        display_numbers = T,
        fontsize_number = 4,
        heatmap_legend_param = list(title = "Spearman"))

    save_png_pdf(p, paste0(plotdir, "rsem_vs_kallisto.", x), height = 10, width = 10)
    print(p)
}

Save session

save.image(file="session.RData")
sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.1 (2025-06-13)
 os       Ubuntu 24.04.3 LTS
 system   x86_64, linux-gnu
 ui       RStudio
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Etc/UTC
 date     2026-02-06
 rstudio  2025.09.2+418 Cucumberleaf Sunflower (server)
 pandoc   3.8.2.1 @ /usr/bin/ (via rmarkdown)
 quarto   1.7.32 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────
 package              * version  date (UTC) lib source
 abind                  1.4-8    2024-09-12 [1] RSPM (R 4.5.0)
 Biobase              * 2.68.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 BiocGenerics         * 0.54.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 BiocParallel           1.42.1   2025-06-01 [1] Bioconductor 3.21 (R 4.5.1)
 bslib                  0.9.0    2025-01-30 [2] RSPM (R 4.5.0)
 cachem                 1.1.0    2024-05-16 [2] RSPM (R 4.5.0)
 circlize               0.4.16   2024-02-20 [1] RSPM (R 4.5.0)
 cli                    3.6.5    2025-04-23 [2] RSPM (R 4.5.0)
 clue                   0.3-66   2024-11-13 [1] RSPM (R 4.5.0)
 cluster                2.1.8.1  2025-03-12 [3] CRAN (R 4.5.1)
 codetools              0.2-20   2024-03-31 [3] CRAN (R 4.5.1)
 colorspace             2.1-1    2024-07-26 [1] RSPM (R 4.5.0)
 ComplexHeatmap         2.24.1   2025-06-25 [1] Bioconductor 3.21 (R 4.5.1)
 cowplot                1.2.0    2025-07-07 [1] RSPM (R 4.5.0)
 crayon                 1.5.3    2024-06-20 [2] RSPM (R 4.5.0)
 data.table             1.17.8   2025-07-10 [2] RSPM (R 4.5.0)
 DelayedArray           0.34.1   2025-04-17 [1] Bioconductor 3.21 (R 4.5.1)
 DESeq2               * 1.48.2   2025-08-27 [1] Bioconductor 3.21 (R 4.5.1)
 digest                 0.6.37   2024-08-19 [2] RSPM (R 4.5.0)
 doParallel             1.0.17   2022-02-07 [1] RSPM (R 4.5.0)
 dplyr                  1.1.4    2023-11-17 [2] RSPM (R 4.5.0)
 EnhancedVolcano      * 1.26.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 evaluate               1.0.5    2025-08-27 [2] RSPM (R 4.5.0)
 farver                 2.1.2    2024-05-13 [2] RSPM (R 4.5.0)
 fastmap                1.2.0    2024-05-15 [2] RSPM (R 4.5.0)
 fastmatch              1.1-6    2024-12-23 [1] RSPM (R 4.5.0)
 fgsea                * 1.34.2   2025-07-13 [1] Bioconductor 3.21 (R 4.5.1)
 foreach                1.5.2    2022-02-02 [1] RSPM (R 4.5.0)
 fs                     1.6.6    2025-04-12 [2] RSPM (R 4.5.0)
 generics             * 0.1.4    2025-05-09 [2] RSPM (R 4.5.0)
 GenomeInfoDb         * 1.44.1   2025-07-23 [1] Bioconductor 3.21 (R 4.5.1)
 GenomeInfoDbData       1.2.14   2025-08-18 [1] Bioconductor
 GenomicRanges        * 1.60.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 GetoptLong             1.0.5    2020-12-15 [1] RSPM (R 4.5.0)
 ggplot2              * 4.0.0    2025-09-11 [2] RSPM (R 4.5.0)
 ggplotify            * 0.1.2    2023-08-09 [1] RSPM (R 4.5.0)
 ggrepel              * 0.9.6    2024-09-07 [1] RSPM (R 4.5.0)
 ggvenn               * 0.1.19   2025-10-08 [1] RSPM
 GlobalOptions          0.1.2    2020-06-10 [1] RSPM (R 4.5.0)
 glue                   1.8.0    2024-09-30 [2] RSPM (R 4.5.0)
 gridGraphics           0.5-1    2020-12-13 [1] RSPM (R 4.5.0)
 gtable                 0.3.6    2024-10-25 [2] RSPM (R 4.5.0)
 htmltools              0.5.8.1  2024-04-04 [2] RSPM (R 4.5.0)
 httr                   1.4.7    2023-08-15 [2] RSPM (R 4.5.0)
 IRanges              * 2.42.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 iterators              1.0.14   2022-02-05 [1] RSPM (R 4.5.0)
 jquerylib              0.1.4    2021-04-26 [2] RSPM (R 4.5.0)
 jsonlite               2.0.0    2025-03-27 [2] RSPM (R 4.5.0)
 knitr                  1.50     2025-03-16 [2] RSPM (R 4.5.0)
 labeling               0.4.3    2023-08-29 [2] RSPM (R 4.5.0)
 lattice                0.22-7   2025-04-02 [3] CRAN (R 4.5.1)
 lifecycle              1.0.4    2023-11-07 [2] RSPM (R 4.5.0)
 locfit                 1.5-9.12 2025-03-05 [1] RSPM (R 4.5.0)
 magrittr               2.0.4    2025-09-12 [2] RSPM (R 4.5.0)
 Matrix                 1.7-3    2025-03-11 [3] CRAN (R 4.5.1)
 MatrixGenerics       * 1.20.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 matrixStats          * 1.5.0    2025-01-07 [1] RSPM (R 4.5.0)
 patchwork            * 1.3.2    2025-08-25 [1] RSPM (R 4.5.0)
 pheatmap               1.0.13   2025-06-05 [1] RSPM (R 4.5.0)
 pillar                 1.11.1   2025-09-17 [2] RSPM (R 4.5.0)
 pkgconfig              2.0.3    2019-09-22 [2] RSPM (R 4.5.0)
 png                    0.1-8    2022-11-29 [2] RSPM (R 4.5.0)
 R6                     2.6.1    2025-02-15 [2] RSPM (R 4.5.0)
 rappdirs               0.3.3    2021-01-31 [2] RSPM (R 4.5.0)
 RColorBrewer         * 1.1-3    2022-04-03 [2] RSPM (R 4.5.0)
 Rcpp                   1.1.0    2025-07-02 [2] RSPM (R 4.5.0)
 rjson                  0.2.23   2024-09-16 [1] RSPM (R 4.5.0)
 rlang                  1.1.6    2025-04-11 [2] RSPM (R 4.5.0)
 rmarkdown              2.30     2025-09-28 [2] RSPM (R 4.5.0)
 rsconnect              1.6.0    2025-10-28 [2] RSPM (R 4.5.0)
 rstudioapi             0.17.1   2024-10-22 [2] RSPM (R 4.5.0)
 S4Arrays               1.8.1    2025-06-01 [1] Bioconductor 3.21 (R 4.5.1)
 S4Vectors            * 0.46.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 S7                     0.2.0    2024-11-07 [1] RSPM (R 4.5.0)
 sass                   0.4.10   2025-04-11 [2] RSPM (R 4.5.0)
 scales                 1.4.0    2025-04-24 [2] RSPM (R 4.5.0)
 sessioninfo            1.2.3    2025-02-05 [2] RSPM (R 4.5.0)
 shape                  1.4.6.1  2024-02-23 [1] RSPM (R 4.5.0)
 SparseArray            1.8.1    2025-07-23 [1] Bioconductor 3.21 (R 4.5.1)
 SummarizedExperiment * 1.38.1   2025-04-30 [1] Bioconductor 3.21 (R 4.5.1)
 tibble                 3.3.0    2025-06-08 [2] RSPM (R 4.5.0)
 tidyselect             1.2.1    2024-03-11 [2] RSPM (R 4.5.0)
 UCSC.utils             1.4.0    2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 vctrs                  0.6.5    2023-12-01 [2] RSPM (R 4.5.0)
 withr                  3.0.2    2024-10-28 [2] RSPM (R 4.5.0)
 xfun                   0.53     2025-08-19 [2] RSPM (R 4.5.0)
 XVector                0.48.0   2025-04-15 [1] Bioconductor 3.21 (R 4.5.1)
 yaml                   2.3.10   2024-07-26 [2] RSPM (R 4.5.0)
 yulab.utils            0.2.1    2025-08-19 [1] RSPM (R 4.5.0)

 [1] /storage/research/dbmr_rubin_lab/R_lib/Rocker_rstudio_4.5.1
 [2] /usr/local/lib/R/site-library
 [3] /usr/local/lib/R/library
 * ── Packages attached to the search path.

──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiV2lsbXMgdHVtb3IsIFNSUDM1ODY5NiBidWxrIFJOQXNlcSBhbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKUHJlbGltaW5hcnkgYW5hbHlzaXMgb2YgdGhlIFNSUDM1ODY5NiBkYXRhc2V0LCB3aGljaCBjb250YWluIFdpbG1zIHR1bW9yIHNhbXBsZXMuCgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQoKbGlicmFyeShERVNlcTIpCmxpYnJhcnkoZmdzZWEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ3JlcGVsKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkoZ2dwbG90aWZ5KQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykKbGlicmFyeShnZ3Zlbm4pCnNvdXJjZSgiL3N0b3JhZ2UvcmVzZWFyY2gvZGJtcl9ydWJpbl9sYWIvc2NyaXB0cy9zYXZlX3BuZ19wZGYuUiIpCnNvdXJjZSgiL3N0b3JhZ2UvcmVzZWFyY2gvZGJtcl9ydWJpbl9sYWIvc2NyaXB0cy9mZ3NlYV9zYXZlX3Jlcy5SIikKCnBsb3RkaXI9KCJwbG90cy8iKQppZighZGlyLmV4aXN0cyhwbG90ZGlyKSl7ZGlyLmNyZWF0ZShwbG90ZGlyLCAgcmVjdXJzaXZlID0gVCl9CgpgYGAKCkxvYWQgc2FtcGxlIG5hbWVzCmBgYHtyfQpzYW1wbGVzIDwtIHNjYW4oIi4uL3NhbXBsZXMudHh0IiwgY2hhcmFjdGVyKCkpCmBgYAoKTG9hZCByc2VtIGNvdW50cwpgYGB7cn0KcnNlbV9yZXNfY29kaW5nIDwtIHJlYWQudGFibGUoIi4uL3JzZW0vYWxsLmdlbmVzLmV4cGVjdGVkX2NvdW50LnJlc3VsdHNfY29kaW5nIiwgaGVhZGVyID0gVCwgc2VwID0gIlx0Iiwgcm93Lm5hbWVzPTEsIGNoZWNrLm5hbWVzID0gRikKCnJzZW1fcmVzX2NvZGluZyRnZW5lX25hbWVfdW5pcSA8LSBtYWtlLnVuaXF1ZShyc2VtX3Jlc19jb2RpbmckZ2VuZV9uYW1lKQoKYGBgCgpyZW1vdmUgbWV0YWRhdGEgY29scyAobm90ZSBuZXcgcnNlbSBoYXMgbW9yZSBjb2xzKQpsYXN0IGNvbCBpcyBnZW5lX25hbWVfdW5pcQpgYGB7cn0KY291bnRzIDwtIHJzZW1fcmVzX2NvZGluZ1tjKC0xOi0yNywgLW5jb2wocnNlbV9yZXNfY29kaW5nKSldCgpyb3duYW1lcyhjb3VudHMpIDwtIHJzZW1fcmVzX2NvZGluZyRnZW5lX25hbWVfdW5pcQpgYGAKCgpjb3VudHMgbmVlZCB0byBiZSBpbnRlZ2VycwpgYGB7cn0KY291bnRzIDwtIHJvdW5kKGNvdW50cykKYGBgCgpyZW1vdmUgbG93IGNvdW50cwpgYGB7cn0KY291bnRzIDwtIGNvdW50c1tyb3dTdW1zKGNvdW50cykgPiA1LF0KYGBgCgoKTG9hZCBtZXRhZGF0cmEKYGBge3J9Cm1ldGEgPC0gb3Blbnhsc3g6OnJlYWQueGxzeCgiLi4vR1NFMjAwMjU2X21ldGFkYXRhX2J1bGtfUk5BX3NlcS54bHN4IikKCm1ldGEkc2FtcGxlIDwtIHN1YigiXyIsICIiLCBtZXRhW1siU2FtcGxlLk5hbWUiXV0pCgpgYGAKCgphc3N1cmUgc2FtZSBvcmRlcgpgYGB7ciBlY2hvID0gVH0KaWRlbnRpY2FsKG1ldGEkc2FtcGxlLCBjb2xuYW1lcyhjb3VudHMpKQpgYGAKIyMjIERFU2VxMgpzZXQgY29sZGF0YQpgYGB7cn0KY29sZGF0YSA8LSBkYXRhLmZyYW1lKGNvbmRpdGlvbiA9IGdzdWIoIiAuKyIsICIiLCBtZXRhJGlzb2xhdGUpLAoJCQkJCQl0aXNzdWUgPSBtZXRhJHRpc3N1ZSwKCQkJCQkJc2V4ID0gbWV0YSRzZXgsCgkJCQkJcm93Lm5hbWVzID0gIGNvbG5hbWVzKGNvdW50cykpCmBgYAoKY29uc3RydWN0IGEgREVTZXFEYXRhU2V0CmBgYHtyfQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudHMsCgkJCQkJCQljb2xEYXRhID0gY29sZGF0YSwKCQkJCQkJCWRlc2lnbiA9IH4gY29uZGl0aW9uICsgdGlzc3VlICsgc2V4KQojcmVsZXZlbApyZWxldmVsKGRkcyRjb25kaXRpb24sIHJlZiA9ICJGYXZvcmFibGUiKQpgYGAKCnJ1biBER0UgaW5mZXJlbmNlCmBgYHtyfQpkZHMgPC0gREVTZXEoZGRzKQpgYGAKClZhcmlhbmNlIHN0YWJpbGl6aW5nIHRyYW5zZm9ybWF0aW9uCmBgYHtyfQp2c2QgPC0gdnN0KGRkcywgYmxpbmQ9RkFMU0UpCmBgYAoKei1zY29yZXMgb2YgdnN0IGRhdGEgKGZvciB2aXN1YWxpc2F0aW9uIGV0Yy4pCmBgYHtyfQp2c2QuenNjb3JlIDwtIGFzLmRhdGEuZnJhbWUodChzY2FsZSh0KGFzc2F5KHZzZCkpLCBzY2FsZT1UUlVFLCBjZW50ZXI9VFJVRSkpKQpgYGAKCiMjIyMgSGVhdG1hcCBvZiB0aGUgc2FtcGxlLXRvLXNhbXBsZSBkaXN0YW5jZXMKYGBge3IgcmVzdWx0cz1GQUxTRX0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHZzZCkpKQpzYW1wbGVEaXN0TWF0cml4IDwtIGFzLm1hdHJpeChzYW1wbGVEaXN0cykKCmNvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKHJldihicmV3ZXIucGFsKDksICJCbHVlcyIpKSApKDI1NSkKCiMgdXNlIGZpeGVkIGNlbGwgc2l6ZXMgdG8gbG9vayBzeW1tZXRyaWNhbCwgdGhlbiBhZGp1c3QgZmlndXJlIHNpemUgdG8gaGF2ZSBwcm9wZXIgbWFyZ2lucwpwIDwtIHBoZWF0bWFwOjpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LAoJCWNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cz1zYW1wbGVEaXN0cywKCQljbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHM9c2FtcGxlRGlzdHMsCgkJY29sPWNvbG9ycywKCQlhbm5vdGF0aW9uID0gY29sZGF0YSwKCQltYWluID0gIlNhbXBsZSBkaXN0YW5jZVxuIiwKCQljZWxsd2lkdGggPSAxMiwgY2VsbGhlaWdodCA9IDEyLAoJCXNpbGVudCA9IFQpCgpzYXZlX3BuZ19wZGYocCwgcGFzdGUwKHBsb3RkaXIsICJoZWF0bWFwIiksIGhlaWdodCA9IDEyLCB3aWR0aCA9IDEyKQpgYGAKYGBge3IgZWNobz1GLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiMgcGhlYXRtYXAgcXVpcmsKcGxvdChwJGd0YWJsZSkKYGBgCgoKCiMjIyMgUENBCmBgYHtyIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTgsIGZpZy5rZWVwPSdhbGwnfQpwY2EgPC0gbGlzdCgiUEMxLVBDMiIgPSAxOjIsCgkJCSJQQzMtUEM0IiA9IDM6NCwKCQkJIlBDNS1QQzYiID0gNTo2LAoJCQkiUEM3LVBDOCIgPSA3OjgsCgkJCSJQQzktUEMxMCIgPSA5OjEwKQoKZm9yICh4IGluIG5hbWVzKHBjYSkpIHsKCWNhdCh4LCAiXG4iKQoJcGNhRGF0YSA8LSBwbG90UENBKHZzZCwgaW50Z3JvdXA9YygiY29uZGl0aW9uIiksIHJldHVybkRhdGE9VFJVRSwgcGNzVG9Vc2UgPSBwY2FbW3hdXSkKCXBlcmNlbnRWYXIgPC0gcm91bmQoMTAwICogYXR0cihwY2FEYXRhLCAicGVyY2VudFZhciIpKQoJCglwIDwtIGdncGxvdChwY2FEYXRhLCBhZXMoISFzeW0ocGFzdGUwKCJQQyIsIHBjYVtbeF1dWzFdKSksICEhc3ltKHBhc3RlMCgiUEMiLCBwY2FbW3hdXVsyXSkpLCBmaWxsPWNvbmRpdGlvbiwgY29sb3I9dGlzc3VlLCBzaGFwZT1zZXgpKSArCgkJZ2VvbV9wb2ludChzaXplPTMsIHN0cm9rZSA9IDEuMikgKwoJCXNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSAyMToyNSkgKyAjIHVzZSBmaWxsYWJsZSBzaGFwZXMKCQlzY2FsZV9jb2xvcl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9ICJTZXQxIiwKCQkJCQkJZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaGFwZSA9IDEpKSkgKyAjIHVzZSBhIGhvbGxvdyBzaGFwZSBpbiBsZWdlbmQKCQlzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJBbmFwbGFzdGljIiA9ICJncmV5MjAiLCAiRmF2b3JhYmxlIiA9ICJncmV5OTAiKSwKCQkJCQkJZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaGFwZSA9IDIzLCBzdHJva2U9MC41KSkpICsgIyB1c2UgZmlsbGFibGUgc2hhcGUgaW4gbGVnZW5kCgkJeGxhYihwYXN0ZTAoIlBDIixwY2FbW3hdXVsxXSwiOiAiLHBlcmNlbnRWYXJbMV0sIiUgdmFyaWFuY2UiKSkgKwoJCXlsYWIocGFzdGUwKCJQQyIscGNhW1t4XV1bMl0sIjogIixwZXJjZW50VmFyWzJdLCIlIHZhcmlhbmNlIikpICsKCQl4bGltKGMoLW1heChhYnMocGNhRGF0YVtbcGFzdGUwKCJQQyIsIHBjYVtbeF1dWzFdKV1dKSktMSwgbWF4KGFicyhwY2FEYXRhW1twYXN0ZTAoIlBDIiwgcGNhW1t4XV1bMV0pXV0pKSsxKSkgKyAjIFBDIGNhbiBiZSBzbWFsbCB0aGF0IGRvdHMgZ2V0IHRyaW1tZWQuIEV4cGFuZCBhIGJpdC4KCQl5bGltKGMoLW1heChhYnMocGNhRGF0YVtbcGFzdGUwKCJQQyIsIHBjYVtbeF1dWzJdKV1dKSktMSwgbWF4KGFicyhwY2FEYXRhW1twYXN0ZTAoIlBDIiwgcGNhW1t4XV1bMl0pXV0pKSsxKSkgKyAjIFBDIGNhbiBiZSBzbWFsbCB0aGF0IGRvdHMgZ2V0IHRyaW1tZWQuIEV4cGFuZCBhIGJpdC4KCQlnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gbmFtZSksCgkJCQkJCXNpemUgPSAxLjUsCgkJCQkJCXNlZ21lbnQuc2l6ZSA9IDAuMSwKCQkJCQkJY29sb3VyID0gJ2dyZXk1MCcsCgkJCQkJCWJveC5wYWRkaW5nICAgPSAwLjEwLAoJCQkJCQlzZWdtZW50LmNvbG9yID0gJ2dyZXk1MCcsCgkJCQkJCWZvcmNlID0gNTAsCgkJCQkJCW1heC5vdmVybGFwcyA9IDMwLAoJCQkJCQlzaG93LmxlZ2VuZCA9IEYpICsKCQl0aGVtZV9taW5pbWFsKCkKCQoJc2F2ZV9wbmdfcGRmKHAsIHBhc3RlMChwbG90ZGlyLCB4KSwgaGVpZ2h0ID0gNSwgd2lkdGggPSA4KQoJcHJpbnQocCkKfQpgYGAKCnZlcmlmeSBjb21wYXJpc29ucwpgYGB7cn0KcmVzdWx0c05hbWVzKGRkcykKYGBgCgpEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzIGZvciBlYWNoIGNvbXBhcmlzb24KVGhlcmUgaXMgb25seSAxIGNvbXBhcmlzb24KYGBge3J9CmMgPC0gIkFuYXBsYXN0aWNfdnNfRmF2b3JhYmxlIgpyZXMgPC0gcmVzdWx0cyhkZHMsIGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwgIkFuYXBsYXN0aWMiLCAiRmF2b3JhYmxlIiksIGFscGhhID0gMC4wNSkKYGBgCgpNQS1wbG90CmBgYHtyfQpzYXZlX3BuZ19wZGYocGxvdE1BKHJlcywgeWxpbT1jKC0xMCwxMCkpLCBwYXN0ZTAocGxvdGRpciwgIkRFU2VxMl9yZXMuIiwgYywgIi5NQS1wbG90IikpCmBgYApgYGB7ciBlY2hvPUZ9CnBsb3RNQShyZXMsIHlsaW09YygtMTAsMTApKQpgYGAKCgoKcmVzIHRvIHRhYmxlLCBhbmQgYWRkIGdlbmUgaW5mbwpgYGB7cn0KcmVzLmRmIDwtIGFzLmRhdGEuZnJhbWUocmVzKQoKCnJlcy5kZiA8LSBtZXJnZShyZXMuZGYsIHJzZW1fcmVzX2NvZGluZ1ssYygiZ2VuZV9uYW1lX3VuaXEiLCAiZ2VuZV9uYW1lIiwgImdlbmVfaWQiLCAiZ2VuZV90eXBlIiwgInNlcW5hbWVzIiwgInN0YXJ0IiwgImVuZCIsICJ3aWR0aCIsICJzdHJhbmQiKV0sIGJ5Lng9MCwgYnkueT0iZ2VuZV9uYW1lX3VuaXEiLCBhbGwueCA9IFQsIGFsbC55ID0gRiwgc29ydCA9IEYpCmNvbG5hbWVzKHJlcy5kZilbMV0gPC0gImdlbmVfbmFtZV91bmlxIgoKYGBgCgpzYXZlIHJlc3VsdHMKYGBge3J9Cm9wZW54bHN4Ojp3cml0ZS54bHN4KHJlcy5kZiwgZmlsZT0iREVTZXEyX3Jlcy54bHN4IikKYGBgCgoKIyMjIFt2b2xjYW5vIHBsb3RzXQpbRW5oYW5jZWRWb2xjYW5vXShodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvRW5oYW5jZWRWb2xjYW5vL2luc3QvZG9jL0VuaGFuY2VkVm9sY2Fuby5odG1sKQpgYGB7ciB3YXJuaW5nPUZBTFNFfQpwIDwtIEVuaGFuY2VkVm9sY2FubyhyZXMuZGYsCgkJCQkJbGFiID0gcmVzLmRmJGdlbmVfbmFtZSwKCQkJCQl4ID0gJ2xvZzJGb2xkQ2hhbmdlJywKCQkJCQl5ID0gJ3BhZGonLAoJCQkJCXlsYWIgPSBicXVvdGUofi1Mb2dbMTBdIH4gaXRhbGljKFBhZGopKSwKCQkJCQl0aXRsZSA9IGMsCgkJCQkJc3VidGl0bGUgPSBOVUxMLAoJCQkJCWN1dG9mZkxpbmVDb2wgPSAiZ3JheTEwIiwKCQkJCQljdXRvZmZMaW5lV2lkdGggPSAwLjIsCgkJCQkJcG9pbnRTaXplID0gMiwKCQkJCQlsYWJTaXplID0gMywKCQkJCQlheGlzTGFiU2l6ZSA9IDE1LAoJCQkJCXRpdGxlTGFiU2l6ZSA9IDE1LAoJCQkJCWxhYkNvbCA9ICJncmF5MzAiLAoJCQkJCWNvbCA9IGMoImdyZXk2MCIsICJzbGF0ZWdyYXkzIiwgImxpZ2h0cGluayIsICJ0b21hdG8iKSwKCQkJCQlsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCgkJCQkJbGVnZW5kTGFiZWxzPWMoIk5TIiwgZXhwcmVzc2lvbihMb2dbMl0gfiBGQyksICJQYWRqIiwgZXhwcmVzc2lvbihQYWRqIH4gYW5kIH4gTG9nWzJdIH4gRkMpKSwKCQkJCQlsZWdlbmRMYWJTaXplID0gMTAsCgkJCQkJbWF4Lm92ZXJsYXBzID0gNTAsCgkJCQkJZHJhd0Nvbm5lY3RvcnMgPSBULAoJCQkJCXdpZHRoQ29ubmVjdG9ycyA9IDAuMSwKCQkJCQlhcnJvd2hlYWRzID0gRiwKCQkJCQljb2xDb25uZWN0b3JzID0gImdyZXkzMCIsCgkJCQkJZGlyZWN0aW9uQ29ubmVjdG9ycz0iYm90aCIgIyAneScgd29ya3Mgb2sgZm9yIGZld2VyIGdlbmVzLiBpZGVhbCBzaG91bGQgYmUgdXByZWd1bGF0ZWQgdG8gb25lIHNpZGUsIGRvd24gdG8gdGhlIG90aGVyCgkJCQkJKSArCgl0aGVtZV9taW5pbWFsKCkKCnNhdmVfcG5nX3BkZihwLCBwYXN0ZTAocGxvdGRpciwgIkVuaGFuY2VkVm9sY2Fuby4iLCBjKSwgaGVpZ2h0ID0gNiwgd2lkdGggPSA4KQpgYGAKCmBgYHtyIGVjaG89RiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9OH0KcHJpbnQocCkKYGBgCgoKCiMjIyBmZ3NlYSBNc2lnZGIKTG9hZCBnZXQgZ2VuZSBzZXRzIGluIGdtdApgYGB7cn0KbXNpZ2RiLmhzLmdtdCA8LSByZWFkUkRTKCIvc3RvcmFnZS9yZXNlYXJjaC9kYm1yX3J1YmluX2xhYi9yZXNvdXJjZXMvbXNpZ2RiL21zaWdkYi5ocy5nbXQucmRzIikKbXNpZ2RiLmhzLmluZm8gPC0gcmVhZFJEUygiL3N0b3JhZ2UvcmVzZWFyY2gvZGJtcl9ydWJpbl9sYWIvcmVzb3VyY2VzL21zaWdkYi9tc2lnZGIuaHMuaW5mby5kZi5yZHMiKQpgYGAKCm1ha2UgcmFuawpgYGB7cn0KcmVzLnJhbmsgPC0gc2V0TmFtZXMocmVzLmRmJHN0YXQsIG1ha2UudW5pcXVlKHJlcy5kZiRnZW5lX25hbWUpKQpgYGAKCgpydW4gb25seSByZWxldmFudCBjb2xsZWN0aW9ucwpgYGB7cn0KY29sbGVjdGlvbnMgPC0gYygiQzIuQ1AiLCAiQzIuQ1A6S0VHR19MRUdBQ1kiLCAiQzIuQ1A6S0VHR19NRURJQ1VTIiwgIkMyLkNQOlJFQUNUT01FIiwgIkM0LjNDQSIsICJDNC5DR04iLCAiQzQuQ00iLCAiQzUuR086QlAiLCAiQzUuR086TUYiLCAiQzYiLCAiQzcuSU1NVU5FU0lHREIiLCAiQzgiLCAiSCIpCmBgYAoKcnVuIGZnc2VhCmBgYHtyfQpyZXMuZmdzZWFSZXMubXNpZ2RiIDwtIHNhcHBseShjb2xsZWN0aW9ucywgZnVuY3Rpb24oeCkgewoJY2F0KGMsIHgsICJcbiIpCglmZ3NlYShwYXRod2F5cyA9IG1zaWdkYi5ocy5nbXRbW3hdXSwKCQkJc3RhdHMgPSByZXMucmFuaywKCQkJZXBzID0gMC4wLCBtaW5TaXplID0gMTUsIG1heFNpemUgPSA1MDApCn0sIHNpbXBsaWZ5PUYpCgojIFNhdmUgc2Vzc2lvbgpzYXZlLmltYWdlKGZpbGU9InNlc3Npb24uUkRhdGEiKQpgYGAKCgpQbG90IGZnc2VhIHJlc3VsdHMKYGBge3J9CmZnc2VhX3NhdmVfcmVzKHJlcy5mZ3NlYVJlcy5tc2lnZGIsCgkJCQlyYW5rID0gcmVzLnJhbmssCgkJCQliYXNlbmFtZSA9ICJmZ3NlYS4iLAoJCQkJZ210ID0gbXNpZ2RiLmhzLmdtdFtjb2xsZWN0aW9uc10sCgkJCQlnbXRfaW5mbyA9IG1zaWdkYi5ocy5pbmZvLAoJCQkJc3VmZml4ID0gYywKCQkJCXN1YnRpdGxlID0gYykKYGBgCgoKIyMgREUgb24gdHJhbnNjcmlwdCBsZXZlbAoKbG9hZCByc2VtIGNvdW50cwpgYGB7cn0KcnNlbV9yZXNfY29kaW5nLnRyYW5zY3JpcHQgPC0gcmVhZC50YWJsZSgiLi4vcnNlbS9hbGwuaXNvZm9ybXMuZXhwZWN0ZWRfY291bnQucmVzdWx0c19jb2RpbmciLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiLCByb3cubmFtZXM9MSwgY2hlY2submFtZXMgPSBGKQoKIyByZW1vdmUgbWV0YWRhdGEgY29scyAobm90ZSBuZXcgcnNlbSBoYXMgbW9yZSBjb2xzKQpjb3VudHMudHJhbnNjcmlwdCA8LSByc2VtX3Jlc19jb2RpbmcudHJhbnNjcmlwdFstMTotMjddCgojIHJvd25hbWVzIGNhbiBiZSB0cmFuc2NyaXB0X25hbWUgKHRoZXkgYXJlIHVuaXF1ZSkKc3VtKGR1cGxpY2F0ZWQocnNlbV9yZXNfY29kaW5nLnRyYW5zY3JpcHQkdHJhbnNjcmlwdF9uYW1lKSkKIyBbMV0gMAojIGJ1dCByZXBsYWNlICItIiB3aXRoICJfIiBzbyBERVNlcTIgZG9lcyBub3QgY29tcGxhaW4Kcm93bmFtZXMoY291bnRzLnRyYW5zY3JpcHQpIDwtIHN1YigiLSIsICJfIiwgcnNlbV9yZXNfY29kaW5nLnRyYW5zY3JpcHQkdHJhbnNjcmlwdF9uYW1lKQpgYGAKClByZXBhcmUgY291bnRzCmBgYHtyfQojIGNvdW50cy50cmFuc2NyaXB0IG5lZWQgdG8gYmUgaW50ZWdlcnMKY291bnRzLnRyYW5zY3JpcHQgPC0gcm91bmQoY291bnRzLnRyYW5zY3JpcHQpCgojIHJlbW92ZSBsb3cgY291bnRzLnRyYW5zY3JpcHQKY291bnRzLnRyYW5zY3JpcHQgPC0gY291bnRzLnRyYW5zY3JpcHRbcm93U3Vtcyhjb3VudHMudHJhbnNjcmlwdCkgPiA1LF0KYGBgCgojIyMgREVTZXEyCmNvbnN0cnVjdCBhIERFU2VxRGF0YVNldApgYGB7cn0KZGRzLnRyYW5zY3JpcHQgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudHMudHJhbnNjcmlwdCwKCQkJCQkJCWNvbERhdGEgPSBjb2xkYXRhLAoJCQkJCQkJZGVzaWduID0gfiBjb25kaXRpb24gKyB0aXNzdWUgKyBzZXgpIAoKIyByZWxldmVsCnJlbGV2ZWwoZGRzLnRyYW5zY3JpcHQkY29uZGl0aW9uLCByZWYgPSAiRmF2b3JhYmxlIikKYGBgCgoKcnVuIERHRSBpbmZlcmVuY2UKYGBge3J9CmRkcy50cmFuc2NyaXB0IDwtIERFU2VxKGRkcy50cmFuc2NyaXB0KQpgYGAKCgpWYXJpYW5jZSBzdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbgpgYGB7cn0KdnNkLnRyYW5zY3JpcHQgPC0gdnN0KGRkcy50cmFuc2NyaXB0LCBibGluZD1GQUxTRSkKYGBgCgp6LXNjb3JlcyBvZiB2c3QgZGF0YSAoZm9yIHZpc3VhbGlzYXRpb24gZXRjLikKYGBge3J9CnZzZC50cmFuc2NyaXB0LnpzY29yZSA8LSBhcy5kYXRhLmZyYW1lKHQoc2NhbGUodChhc3NheSh2c2QudHJhbnNjcmlwdCkpLCBzY2FsZT1UUlVFLCBjZW50ZXI9VFJVRSkpKQpgYGAKCgpIZWF0bWFwIG9mIHRoZSBzYW1wbGUtdG8tc2FtcGxlIGRpc3RhbmNlcwpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHZzZC50cmFuc2NyaXB0KSkpCnNhbXBsZURpc3RNYXRyaXggPC0gYXMubWF0cml4KHNhbXBsZURpc3RzKQoKY29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUocmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQoKIyB1c2UgZml4ZWQgY2VsbCBzaXplcyB0byBsb29rIHN5bW1ldHJpY2FsLCB0aGVuIGFkanVzdCBmaWd1cmUgc2l6ZSB0byBoYXZlIHByb3BlciBtYXJnaW5zCnAgPC0gcGhlYXRtYXA6OnBoZWF0bWFwKHNhbXBsZURpc3RNYXRyaXgsCgkJY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzPXNhbXBsZURpc3RzLAoJCWNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scz1zYW1wbGVEaXN0cywKCQljb2w9Y29sb3JzLAoJCWFubm90YXRpb24gPSBjb2xkYXRhLAoJCW1haW4gPSAiU2FtcGxlIGRpc3RhbmNlXG4iLAoJCWNlbGx3aWR0aCA9IDEyLCBjZWxsaGVpZ2h0ID0gMTIsCgkJc2lsZW50PVQpCgpzYXZlX3BuZ19wZGYocCwgcGFzdGUwKHBsb3RkaXIsICJoZWF0bWFwLnRyYW5zY3JpcHQiKSwgaGVpZ2h0ID0gMTIsIHdpZHRoID0gMTIpCmBgYAoKYGBge3IgZWNobz1GLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiMgcGhlYXRtYXAgcXVpcmsKcGxvdChwJGd0YWJsZSkKYGBgCgpQQ0EKYGBge3IgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OCwgZmlnLmtlZXA9J2FsbCd9CnBjYURhdGEgPC0gcGxvdFBDQSh2c2QudHJhbnNjcmlwdCwgaW50Z3JvdXA9YygiY29uZGl0aW9uIiksIHJldHVybkRhdGE9VFJVRSkKcGVyY2VudFZhciA8LSByb3VuZCgxMDAgKiBhdHRyKHBjYURhdGEsICJwZXJjZW50VmFyIikpCgpwIDwtIGdncGxvdChwY2FEYXRhLCBhZXMoUEMxLCBQQzIsIGZpbGw9Y29uZGl0aW9uLCBjb2xvcj10aXNzdWUsIHNoYXBlPXNleCkpICsKCWdlb21fcG9pbnQoc2l6ZT0zLCBzdHJva2UgPSAxLjIpICsKCXNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSAyMToyNSkgKyAjIHVzZSBmaWxsYWJsZSBzaGFwZXMKCXNjYWxlX2NvbG9yX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gIlNldDEiLAoJCQkJCWd1aWRlID0gZ3VpZGVfbGVnZW5kKG92ZXJyaWRlLmFlcyA9IGxpc3Qoc2hhcGUgPSAxKSkpICsgIyB1c2UgYSBob2xsb3cgc2hhcGUgaW4gbGVnZW5kCglzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJBbmFwbGFzdGljIiA9ICJncmV5MjAiLCAiRmF2b3JhYmxlIiA9ICJncmV5OTAiKSwKCQkJCQlndWlkZSA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNoYXBlID0gMjMsIHN0cm9rZT0wLjUpKSkgKyAjIHVzZSBmaWxsYWJsZSBzaGFwZSBpbiBsZWdlbmQKCXhsYWIocGFzdGUwKCJQQzE6ICIscGVyY2VudFZhclsxXSwiJSB2YXJpYW5jZSIpKSArCgl5bGFiKHBhc3RlMCgiUEMyOiAiLHBlcmNlbnRWYXJbMl0sIiUgdmFyaWFuY2UiKSkgKwoJeWxpbShjKC1tYXgoYWJzKHBjYURhdGEkUEMyKSktMSwgbWF4KGFicyhwY2FEYXRhJFBDMikpKzEpKSArICMgaWYgUEMyIGlzIHNtYWxsIGRvdHMgZ2V0IHRyaW1tZWQuIEV4cGFuZCBhIGJpdC4KCWdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBuYW1lKSwKCQkJCQlzaXplID0gMS41LAoJCQkJCXNlZ21lbnQuc2l6ZSA9IDAuMSwKCQkJCQljb2xvdXIgPSAnZ3JleTUwJywKCQkJCQlib3gucGFkZGluZyAgID0gMC4xMCwKCQkJCQlzZWdtZW50LmNvbG9yID0gJ2dyZXk1MCcsCgkJCQkJZm9yY2UgPSA1MCwKCQkJCQltYXgub3ZlcmxhcHMgPSAzMCwKCQkJCQlzaG93LmxlZ2VuZCA9IEYpICsKCXRoZW1lX21pbmltYWwoKQoKc2F2ZV9wbmdfcGRmKHAsIHBhc3RlMChwbG90ZGlyLCAiUENBLnRyYW5zY3JpcHQiKSwgaGVpZ2h0ID0gNSwgd2lkdGggPSA4KQpwcmludChwKQpgYGAKCkZldGNoIHJlc3VsdHMKYGBge3J9CnJlcy50cmFuc2NyaXB0IDwtIHJlc3VsdHMoZGRzLnRyYW5zY3JpcHQsIGNvbnRyYXN0ID0gYygiY29uZGl0aW9uIiwgIkFuYXBsYXN0aWMiLCAiRmF2b3JhYmxlIiksIGFscGhhID0gMC4wNSkKYGBgCgpNQS1wbG90CmBgYHtyfQpzYXZlX3BuZ19wZGYocGxvdE1BKHJlcy50cmFuc2NyaXB0LCB5bGltPWMoLTIwLDIwKSksIHBhc3RlMChwbG90ZGlyLCAiREVTZXEyX3Jlcy50cmFuc2NyaXB0LiIsIGMsICIuTUEtcGxvdCIpKQpgYGAKCmBgYHtyIGVjaG89Rn0KcGxvdE1BKHJlcy50cmFuc2NyaXB0LCB5bGltPWMoLTEwLDEwKSkKYGBgCgpyZXMgdG8gdGFibGUsIGFuZCBhZGQgZ2VuZSBpbmZvCmBgYHtyfQpyZXMudHJhbnNjcmlwdC5kZiA8LSBhcy5kYXRhLmZyYW1lKHJlcy50cmFuc2NyaXB0KQoKIyBfIHRvIC0gc28gdGhhdCB3ZSBjYW4gbWVyZ2UKcmVzLnRyYW5zY3JpcHQuZGYkdHJhbnNjcmlwdF9uYW1lIDwtIHN1YigiXyIsICItIiwgcm93bmFtZXMocmVzLnRyYW5zY3JpcHQpKQoKcmVzLnRyYW5zY3JpcHQuZGYgPC0gbWVyZ2UocmVzLnRyYW5zY3JpcHQuZGYsIHJzZW1fcmVzX2NvZGluZy50cmFuc2NyaXB0WyxjKCJ0cmFuc2NyaXB0X25hbWUiLCAidHJhbnNjcmlwdF9pZCIsICJ0cmFuc2NyaXB0X3R5cGUiLCAiZ2VuZV9uYW1lIiwgImdlbmVfaWQiLCAic2VxbmFtZXMiLCAic3RhcnQiLCAiZW5kIiwgIndpZHRoIiwgInN0cmFuZCIpXSwgYnk9InRyYW5zY3JpcHRfbmFtZSIsIGFsbC54ID0gVCwgYWxsLnkgPSBGLCBzb3J0ID0gRikKYGBgCgpzYXZlIHJlc3VsdHMKYGBge3J9Cm9wZW54bHN4Ojp3cml0ZS54bHN4KHJlcy50cmFuc2NyaXB0LmRmLCBmaWxlPSJERVNlcTJfcmVzLnRyYW5zY3JpcHQueGxzeCIpCmBgYAoKCiMjIyB2b2xjYW5vIHBsb3RzCltFbmhhbmNlZFZvbGNhbm9dKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9FbmhhbmNlZFZvbGNhbm8vaW5zdC9kb2MvRW5oYW5jZWRWb2xjYW5vLmh0bWwpCmBgYHtyfQpwIDwtIEVuaGFuY2VkVm9sY2FubyhyZXMudHJhbnNjcmlwdC5kZiwKCQkJCQlsYWIgPSByZXMudHJhbnNjcmlwdC5kZiR0cmFuc2NyaXB0X25hbWUsCgkJCQkJeCA9ICdsb2cyRm9sZENoYW5nZScsCgkJCQkJeSA9ICdwYWRqJywKCQkJCQl5bGFiID0gYnF1b3RlKH4tTG9nWzEwXSB+IGl0YWxpYyhQYWRqKSksCgkJCQkJdGl0bGUgPSBjLAoJCQkJCXN1YnRpdGxlID0gTlVMTCwKCQkJCQljdXRvZmZMaW5lQ29sID0gImdyYXkxMCIsCgkJCQkJY3V0b2ZmTGluZVdpZHRoID0gMC4yLAoJCQkJCXBvaW50U2l6ZSA9IDIsCgkJCQkJbGFiU2l6ZSA9IDIsCgkJCQkJYXhpc0xhYlNpemUgPSAxNSwKCQkJCQl0aXRsZUxhYlNpemUgPSAxNSwKCQkJCQlsYWJDb2wgPSAiZ3JheTMwIiwKCQkJCQljb2wgPSBjKCJncmV5NjAiLCAic2xhdGVncmF5MyIsICJsaWdodHBpbmsiLCAidG9tYXRvIiksCgkJCQkJbGVnZW5kUG9zaXRpb24gPSAncmlnaHQnLAoJCQkJCWxlZ2VuZExhYmVscz1jKCJOUyIsIGV4cHJlc3Npb24oTG9nWzJdIH4gRkMpLCAiUGFkaiIsIGV4cHJlc3Npb24oUGFkaiB+IGFuZCB+IExvZ1syXSB+IEZDKSksCgkJCQkJbGVnZW5kTGFiU2l6ZSA9IDEwLAoJCQkJCW1heC5vdmVybGFwcyA9IDUwLAoJCQkJCWRyYXdDb25uZWN0b3JzID0gVCwKCQkJCQl3aWR0aENvbm5lY3RvcnMgPSAwLjEsCgkJCQkJYXJyb3doZWFkcyA9IEYsCgkJCQkJY29sQ29ubmVjdG9ycyA9ICJncmV5MzAiLAoJCQkJCSkgKwoJdGhlbWVfbWluaW1hbCgpCgpzYXZlX3BuZ19wZGYocCwgcGFzdGUwKHBsb3RkaXIsICJFbmhhbmNlZFZvbGNhbm8udHJhbnNjcmlwdC4iLCBjKSwgaGVpZ2h0ID0gNiwgd2lkdGggPSA4KQpgYGAKCgpgYGB7ciBlY2hvPUYsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CnByaW50KHApCmBgYAoKCgojIyMgS2FsbGlzdG8KbG9hZCBrYWxsaXN0byBjb3VudHMgb3ZlciB0cmFuc2NyaXB0cwpgYGB7cn0Ka2FsbGlzdG9fcmVzLnRyYW5zY3JpcHQgPC0gcmVhZC50YWJsZSgiLi4va2FsbGlzdG8vYWxsLmVzdF9jb3VudC50eHQuZ3oiLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiLCByb3cubmFtZXM9MSwgY2hlY2submFtZXMgPSBGKVstMV0gIyBvbWl0IGNvbHVtbiAibGVuZ3RoIgpgYGAKCiMjIyMgdXNlIHR4aW1wb3J0IHRvIGFnZ3JlZ2F0ZSB0byBnZW5lIGxldmVsCiMjIyMjIFByZXBhcmUgZGF0YQpyZWFkIHRoZSBndGYgdXNlZCBmb3IgdGhlIEthbGxpc3RvIGluZGV4IChmdWxsIGFubm8sIG5vdCBqdXN0ICJhbm5vdGF0aW9uIiBvciAicHJpbWFyeV9hc3NlbWJseS5hbm5vdGF0aW9uIikKYGBge3J9CmdlbmNvZGUudjQ4IDwtIHJ0cmFja2xheWVyOjpyZWFkR0ZGKCIvc3RvcmFnZS9yZXNlYXJjaC9kYm1yX3J1YmluX2xhYi9waXBlbGluZS9yZWYvYW5uby9oZzM4L2dlbmNvZGUudjQ4LmNocl9wYXRjaF9oYXBsX3NjYWZmLmFubm90YXRpb24uZ3RmLmd6IikKYGBgCgpkaWQgd2UgZmV0Y2ggZXZlcnl0aGluZz8KYGBge3J9CnN1bSghcm93bmFtZXMoa2FsbGlzdG9fcmVzLnRyYW5zY3JpcHQpICVpbiUgZ2VuY29kZS52NDgkdHJhbnNjcmlwdF9pZCkKYGBgCgptb3JlIHNhbml0eSBjaGVja3MKYGBge3J9CnJzZW1fdHJhbnNjcmlwdF9saXN0IDwtIHJlYWQudGFibGUoIi4uL3JzZW0vYWxsLmlzb2Zvcm1zLmV4cGVjdGVkX2NvdW50LnJlc3VsdHMiLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiKVtbMV1dCnN1bShyc2VtX3RyYW5zY3JpcHRfbGlzdCAlaW4lIGdlbmNvZGUudjQ4JHRyYW5zY3JpcHRfaWQpCmxlbmd0aChyc2VtX3RyYW5zY3JpcHRfbGlzdCkKYGBgCgptYWtlIGEgZGF0YS5mcmFtZSBjYWxsZWQgdHgyZ2VuZSB3aXRoIHR3byBjb2x1bW5zOiAxKSB0cmFuc2NyaXB0IElEIGFuZCAyKSBnZW5lIElELiBUaGUgY29sdW1uIG5hbWVzIGRvIG5vdCBtYXR0ZXIgYnV0IHRoaXMgY29sdW1uIG9yZGVyIG11c3QgYmUgdXNlZC4KYGBge3J9CnR4MmdlbmUgPC0gZ2VuY29kZS52NDhbYygidHJhbnNjcmlwdF9pZCIsICJnZW5lX2lkIildCgojIHJlbW92ZSBOQSBhbmQgZHVwbGljYXRlIHRyYW5zY3JpcHRzCnR4MmdlbmUgPC0gbmEub21pdCh0eDJnZW5lKQp0eDJnZW5lIDwtIHR4MmdlbmVbIWR1cGxpY2F0ZWQodHgyZ2VuZSR0cmFuc2NyaXB0X2lkKSxdCmBgYAoKZ2VuZSBjb3VudHMgZnJvbSBrYWxsaXN0byBUU1YgZmlsZXMKYGBge3J9CmtfZmlsZXMgPC0gZmlsZS5wYXRoKCIuLi9rYWxsaXN0byIsIHNhbXBsZXMsICJhYnVuZGFuY2UudHN2Lmd6IikKbmFtZXMoa19maWxlcykgPC0gc2FtcGxlcwpgYGAKCnR4aW1wb3J0IHRvIGFnZ3JlZ2F0ZSB0byBnZW5lIGxldmVsCmBgYHtyfQojIHdlIGRvbid0IG5lZWQgaWdub3JlQWZ0ZXJCYXIKa2FsbGlzdG9fcmVzLmdlbmUgPC0gdHhpbXBvcnQ6OnR4aW1wb3J0KGtfZmlsZXMsIHR5cGUgPSAia2FsbGlzdG8iLCB0eDJnZW5lID0gdHgyZ2VuZSkKCiMgdG8gZGYKa2FsbGlzdG9fcmVzLmdlbmUuZGYgPC0gYXMuZGF0YS5mcmFtZShrYWxsaXN0b19yZXMuZ2VuZVtbImFidW5kYW5jZSJdXSkKYGBgCgoKIyMjIGNvbXBhcmUgcnNlbSBhbmQga2FsbGlzdG8KYGBge3J9CnJzZW1fcmVzIDwtIHJlYWQudGFibGUoIi4uL3JzZW0vYWxsLmdlbmVzLmV4cGVjdGVkX2NvdW50LnJlc3VsdHMiLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiLCByb3cubmFtZXM9MSwgY2hlY2submFtZXMgPSBGKVtjKC0xOi0yNyldICMgb21pdCBtZXRhZGF0YSBjb2xzCgpyc2VtX3Jlcy50cmFuc2NyaXB0IDwtIHJlYWQudGFibGUoIi4uL3JzZW0vYWxsLmlzb2Zvcm1zLmV4cGVjdGVkX2NvdW50LnJlc3VsdHMiLCBoZWFkZXIgPSBULCBzZXAgPSAiXHQiLCByb3cubmFtZXM9MSwgY2hlY2submFtZXMgPSBGKVtjKC0xOi0yNyldICMgb21pdCBtZXRhZGF0YSBjb2xzCmBgYAoKc2FuaXR5IGNoZWNrCmBgYHtyfQppZGVudGljYWwoY29sbmFtZXMocnNlbV9yZXMudHJhbnNjcmlwdCksIGNvbG5hbWVzKGthbGxpc3RvX3Jlcy50cmFuc2NyaXB0KSkKYGBgCgoKTm90ZToga2FsbGlzdG8gaGFzIG1vcmUgZ2VuZXMvdHJhbnNjcmlwdHMgdGhhbiByc2VtICh3aGljaCBhcmUgYWxsIGluIGthbGxpc3RvKQpgYGB7cn0KcnNlbV92c19rYWxsaXN0byA8LSBsaXN0KHRyYW5zY3JpcHQgPSBjb3IocnNlbV9yZXMudHJhbnNjcmlwdCwga2FsbGlzdG9fcmVzLnRyYW5zY3JpcHRbcm93bmFtZXMocnNlbV9yZXMudHJhbnNjcmlwdCksXSwgbWV0aG9kID0gInNwZWFybWFuIikpCgpyc2VtX3ZzX2thbGxpc3RvW1sidHJhbnNjcmlwdC5jb2RpbmciXV0gPC0gY29yKHJzZW1fcmVzX2NvZGluZy50cmFuc2NyaXB0Wy0xOi0yN10sIGthbGxpc3RvX3Jlcy50cmFuc2NyaXB0W3Jvd25hbWVzKHJzZW1fcmVzX2NvZGluZy50cmFuc2NyaXB0KSxdLCBtZXRob2QgPSAic3BlYXJtYW4iKQoKcnNlbV92c19rYWxsaXN0b1tbImdlbmUiXV0gPC0gY29yKHJzZW1fcmVzLCBrYWxsaXN0b19yZXMuZ2VuZS5kZltyb3duYW1lcyhyc2VtX3JlcyksXSwgbWV0aG9kID0gInNwZWFybWFuIikKCnJzZW1fdnNfa2FsbGlzdG9bWyJnZW5lLmNvZGluZyJdXSA8LSBjb3IocnNlbV9yZXNbcm93bmFtZXMocnNlbV9yZXNfY29kaW5nKSxdLCBrYWxsaXN0b19yZXMuZ2VuZS5kZltyb3duYW1lcyhyc2VtX3Jlc19jb2RpbmcpLF0sIG1ldGhvZCA9ICJzcGVhcm1hbiIpCmBgYAoKUGxvdCBjb3JyZWxhdG9pb25zCmBgYHtyIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMCwgZmlnLmtlZXA9J2FsbCd9CmZvciAoeCBpbiBuYW1lcyhyc2VtX3ZzX2thbGxpc3RvKSkgewoJcCA8LSBDb21wbGV4SGVhdG1hcDo6cGhlYXRtYXAocnNlbV92c19rYWxsaXN0b1tbeF1dLAoJCWNsdXN0ZXJfcm93cyA9IEYsCgkJY2x1c3Rlcl9jb2xzID0gRiwKCQltYWluID0gcGFzdGUwKCJSU0VNIHZzLiBLYWxsaXN0byIsICJcbiIsICJjb3VudHMiLCAiXG4iLCB4KSwKCQlmb250c2l6ZV9yb3cgPSA4LAoJCWZvbnRzaXplX2NvbCA9IDgsCgkJY2VsbGhlaWdodCA9IDEyLAoJCWNlbGx3aWR0aCA9IDEyLAoJCWRpc3BsYXlfbnVtYmVycyA9IFQsCgkJZm9udHNpemVfbnVtYmVyID0gNCwKCQloZWF0bWFwX2xlZ2VuZF9wYXJhbSA9IGxpc3QodGl0bGUgPSAiU3BlYXJtYW4iKSkKCglzYXZlX3BuZ19wZGYocCwgcGFzdGUwKHBsb3RkaXIsICJyc2VtX3ZzX2thbGxpc3RvLiIsIHgpLCBoZWlnaHQgPSAxMCwgd2lkdGggPSAxMCkKCXByaW50KHApCn0KYGBgCgpTYXZlIHNlc3Npb24KYGBge3J9CnNhdmUuaW1hZ2UoZmlsZT0ic2Vzc2lvbi5SRGF0YSIpCmBgYAoKYGBge3J9CnNlc3Npb25pbmZvOjpzZXNzaW9uX2luZm8oKQpgYGA=